#include "pch.h"

#include "DXGIimpl.h"

using namespace DX4WRT::DXGI;

IFactory2^ DXGI::CreateFactory2 ()
{
	IDXGIFactory2* pFactory;

	HRESULT hr = CreateDXGIFactory1 (__uuidof(IDXGIFactory2), (void**)&pFactory);

	if (hr != S_OK)
		throw ref new Platform::COMException (hr);

	return ref new Factory (pFactory);
}

IDevice2^ DXGI::GetDeviceForObject (Platform::IntPtr obj)
{
	IUnknown* pUnk = (IUnknown*)(void*)obj;

	IDXGIDevice2* pDXGIDevice;

	HRESULT hr = pUnk->QueryInterface (__uuidof(IDXGIDevice2), (void**)&pDXGIDevice);

	if (hr != S_OK)
		throw ref new Platform::COMException (hr);

	return ref new DX4WRT::DXGI::Device (pDXGIDevice);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

Factory::Factory (IDXGIFactory2 * pFactory)
	: m_pFactory (pFactory)
{
}

Factory::~Factory ()
{
	if (m_pFactory != nullptr)
	{
		m_pFactory->Release ();
		m_pFactory = nullptr;
	}
}

IAdapter^ Factory::EnumAdapters (uint32 adapter)
{
	return EnumAdapters1 (adapter);
}

IAdapter1^ Factory::EnumAdapters1 (uint32 adapter)
{
	IDXGIAdapter1* pAdapter;

	HRESULT hr = m_pFactory->EnumAdapters1 (adapter, &pAdapter);

	if (hr == DXGI_ERROR_NOT_FOUND)
		return nullptr;

	if (hr != S_OK)
		throw ref new Platform::COMException (hr);

	// need shared storage
	return ref new Adapter (pAdapter);
}

ISwapChain1^ Factory::CreateSwapChainForImmersiveWindow (IDevice^ device, Platform::Object^ window, SwapChainDescription1^ description, IOutput^ RestrictToOutput)
{
	IUnknown* pWindow = (IUnknown*) reinterpret_cast<__cli_IUnknown*>(window);

	IDXGIDevice* pDevice = ((DX4WRT::DXGI::Device^)device)->GetNative();

	IDXGISwapChain1* pSwapChain;

	HRESULT hr = m_pFactory->CreateSwapChainForImmersiveWindow(pDevice, pWindow, &description->m_Desc, nullptr, &pSwapChain);

	if (hr != S_OK)
		throw ref new Platform::COMException (hr);

	return ref new SwapChain (pSwapChain);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

Adapter::Adapter (IDXGIAdapter1* pAdapter)
	: m_pAdapter (pAdapter)
	, m_pDesc (nullptr)
{
}

Adapter::~Adapter ()
{
	if (m_pAdapter != nullptr)
	{
		m_pAdapter->Release ();
		m_pAdapter = nullptr;
	}
}

void Adapter::GetInternal (Platform::IntPtr* pVal)
{
	*pVal = Platform::IntPtr(m_pAdapter);
}

Platform::String^ Adapter::Description::get ()
{
	UpdateDescription ();
	return ref new Platform::String(m_pDesc->Description);
}

uint32 Adapter::VendorId::get ()
{
	UpdateDescription ();
	return m_pDesc->VendorId;
}

uint32 Adapter::DeviceId::get ()
{
	UpdateDescription ();
	return m_pDesc->DeviceId;
}

uint32 Adapter::SubSysId::get ()
{
	UpdateDescription ();
	return m_pDesc->SubSysId;
}

uint32 Adapter::Revision::get ()
{
	UpdateDescription ();
	return m_pDesc->Revision;
}

uint64 Adapter::DedicatedVideoMemory::get ()
{
	UpdateDescription ();
	return m_pDesc->Revision;
}

uint64 Adapter::DedicatedSystemMemory::get ()
{
	UpdateDescription ();
	return m_pDesc->DedicatedSystemMemory;
}

uint64 Adapter::SharedSystemMemory::get ()
{
	UpdateDescription ();
	return m_pDesc->SharedSystemMemory;
}

AdapterOptions Adapter::Flags::get ()
{
	UpdateDescription ();
	return (AdapterOptions)m_pDesc->Flags;
}

void Adapter::UpdateDescription ()
{
	if (m_pDesc != nullptr)
		return;

	m_pDesc = new DXGI_ADAPTER_DESC1;

	HRESULT hr = m_pAdapter->GetDesc1 (m_pDesc);

	if (hr != S_OK)
	{
		delete m_pDesc;
		throw ref new Platform::COMException (hr);
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////

Device::Device (IDXGIDevice2* pDevice)
	: m_pDevice (pDevice)
{
}

Device::~Device ()
{
	if (m_pDevice != nullptr)
	{
		m_pDevice->Release ();
		m_pDevice = nullptr;
	}
}

IDXGIDevice2* Device::GetNative()
{
	return m_pDevice;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

SwapChain::SwapChain (IDXGISwapChain1* pSwapChain)
	: m_pSwapChain (pSwapChain)
{
}

SwapChain::~SwapChain ()
{
	if (m_pSwapChain != nullptr)
	{
		m_pSwapChain->Release ();
		m_pSwapChain = nullptr;
	}
}

void SwapChain::Present (uint32 syncInterval, PresentOptions flags)
{
	m_pSwapChain->Present (syncInterval, (UINT)flags);
}

void SwapChain::GetInternal (Platform::IntPtr* pVal)
{
	*pVal = Platform::IntPtr (m_pSwapChain);
}

